package ldh.msg.helper;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import ldh.msg.MsgConfigInfo;
import ldh.msg.MsgManager;
import ldh.msg.socket.MsgUDPManager;


import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Structure;
import com.sun.jna.examples.unix.X11;
import com.sun.jna.win32.StdCallLibrary;

public class AbsentChecker extends Thread {
	private static AbsentChecker checker = null;
	private static int absentTime = 1 * 60;
	
	private static boolean operFlag = false;
	
	private AbsentChecker() {
		
	}
	
	public static AbsentChecker getInstance() {
		if (checker == null) {
			synchronized (AbsentChecker.class) {
				checker = new AbsentChecker();
			}
		}
		return checker;
	}
	
	public void destroy() {
		checker = null;
	}
	
	public interface Kernel32 extends StdCallLibrary { 
        Kernel32 INSTANCE = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
        
        public int GetTickCount();
    };
    
	public interface User32 extends StdCallLibrary { 
        User32 INSTANCE = (User32)Native.loadLibrary("user32", User32.class);
        
        public static class LASTINPUTINFO extends Structure {
            public int cbSize = 8;
            
            /// Tick count of when the last input event was received.
            public int dwTime;
        }
        public boolean GetLastInputInfo(LASTINPUTINFO result);
    };
    
    private int getIdleTimeMillisWin32() {
        User32.LASTINPUTINFO lastInputInfo = new User32.LASTINPUTINFO();
        User32.INSTANCE.GetLastInputInfo(lastInputInfo);
        return Kernel32.INSTANCE.GetTickCount() - lastInputInfo.dwTime;
    }
	
	enum State {
        UNKNOWN, ONLINE, IDLE, AWAY
    };
    
    interface Xss extends Library {
		Xss INSTANCE = (Xss)Native.loadLibrary("Xss", Xss.class);
		
		public class XScreenSaverInfo extends Structure
		{
			public X11.Window window; /* screen saver window */
			public int state; /* ScreenSaver{Off,On,Disabled} */
			public int kind; /* ScreenSaver{Blanked,Internal,External} */
			public NativeLong til_or_since; /* milliseconds */
			public NativeLong idle; /* milliseconds */
			public NativeLong event_mask; /* events */
		}
	
		XScreenSaverInfo XScreenSaverAllocInfo();
		int XScreenSaverQueryInfo(X11.Display dpy, X11.Drawable drawable, XScreenSaverInfo saver_info);
	}
	
	private int getIdleTimeMillis() {
		X11.Window win=null; Xss.XScreenSaverInfo info=null; X11.Display dpy=null;
		final X11 x11 = X11.INSTANCE; final Xss xss = Xss.INSTANCE;
		
		long idlemillis = 0L;
		try
		{
			dpy = x11.XOpenDisplay(null);
			win = x11.XDefaultRootWindow(dpy);
			info = xss.XScreenSaverAllocInfo();
			xss.XScreenSaverQueryInfo(dpy, win, info);
			
			idlemillis = info.idle.longValue();
		}
		finally
		{
			if(info != null)
			x11.XFree(info.getPointer());
			info = null;
			
			if(dpy != null)
			x11.XCloseDisplay(dpy);
			dpy = null;
		}
		return (int)idlemillis;
	}

	public static void setAbsentTime(int absentTime) {
		AbsentChecker.absentTime = absentTime;
	}

	public static void setOperFlag(boolean operFlag) {
		AbsentChecker.operFlag = operFlag;
	}

	public void run() {
		try {
			System.out.println("System.getProperty :  " + System.getProperty("os.name"));
			System.out.println("MsgConfigInfo.getInstance().getAbsent() :  " + MsgConfigInfo.getInstance().getAbsent());
			absentTime = MsgConfigInfo.getInstance().getAbsent() * 60;
	
	    	if (System.getProperty("os.name").contains("Windows") || System.getProperty("os.name").contains("Linux")) {
		    	State state = State.UNKNOWN;
		    	State newState = State.UNKNOWN;
		        State oldState = State.UNKNOWN;
		        DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
		        int idleSec = 0;
		        if (absentTime > 0) {
		        	operFlag = true;
		        }
		        
		    	while (operFlag) {
		            if (System.getProperty("os.name").contains("Windows")) {
		            	idleSec = getIdleTimeMillisWin32() / 1000;
		            } else if (System.getProperty("os.name").contains("Linux")) {
		            	idleSec = getIdleTimeMillis() / 1000;
		            }
		            newState = idleSec < 30 ? State.ONLINE : idleSec > absentTime ? State.AWAY : State.IDLE;
	                try {
			            if (newState != state) {
			                state = newState;
			                System.out.println(dateFormat.format(new Date()) + " # " + state);
			                if (state == State.AWAY) {
			                	//Absent Operation
		                		MsgManager.getInstance().setCurrentAbsentFlag(true);
		                		MsgUDPManager.getInstance().sendBroadcast(MsgManager.getInstance().getMyAlias());
		                		oldState = State.AWAY;
			                } else if (state == State.ONLINE) {
			                	if (oldState == State.AWAY) {
			                		MsgManager.getInstance().setCurrentAbsentFlag(false);
			                		MsgUDPManager.getInstance().sendBroadcast(MsgManager.getInstance().getMyAlias());
			                	}
			                	oldState = State.ONLINE;
			                } else {
			                	oldState = State.UNKNOWN;
			                }
			            }
	                } catch (Exception e) {
                		e.printStackTrace();
                	}
		            try { Thread.sleep(2000); } catch (Exception ex) {}
		        }
		    	 System.out.println("stop AbsentChecker");
	    	}
		} catch (Exception e) {
    		e.printStackTrace();
    	}
    }
}
